package cn.yunsenlin.formula.cloud.auth;

import cn.yunsenlin.formula.cloud.bean.auth.AuthResult;
import cn.yunsenlin.formula.cloud.consts.AuthConst;
import cn.yunsenlin.formula.cloud.interfaces.AuthService;
import cn.yunsenlin.formula.cloud.mysql.bean.generator.TbGroup;
import cn.yunsenlin.formula.cloud.mysql.bean.generator.TbUser;
import cn.yunsenlin.formula.cloud.mysql.bean.generator.TbUserCriteria;
import cn.yunsenlin.formula.cloud.mysql.mapper.generator.TbGroupMapper;
import cn.yunsenlin.formula.cloud.mysql.mapper.generator.TbUserMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Service;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpSession;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.List;

import static cn.yunsenlin.formula.cloud.consts.AuthConst.AUTH_RESULT_SESSION_NAME;
import static cn.yunsenlin.formula.cloud.consts.AuthConst.TOKEN_KEY;

@Service
public class AuthServiceImpl implements AuthService {
    private final TbUserMapper tbUserMapper;
    private final TbGroupMapper tbGroupMapper;

    @SuppressWarnings("all")
    public AuthServiceImpl(TbUserMapper tbUserMapper, TbGroupMapper tbGroupMapper) {
        this.tbUserMapper = tbUserMapper;
        this.tbGroupMapper = tbGroupMapper;
    }

    @Override
    public AuthResult checkAuthInfo(TbUser tbUser) {
        TbUserCriteria criteria = new TbUserCriteria();
        criteria.or().andUsernameEqualTo(tbUser.getUsername()).andPasswordEqualTo(tbUser.getPassword());
        List<TbUser> tbUsers = tbUserMapper.selectByExample(criteria);
        if (tbUsers.size() >= 1) {
            return generateSuccessAuthResult(tbUsers.get(0).getId());
        }
        return generateFailAuthResult();
    }

    @Override
    public AuthResult checkToken(String token) {
        Claims claims = getClaimsByToken(token);
        if (claims.getExpiration().getTime() - new Date().getTime() > 0) {
            return generateSuccessAuthResult(Integer.parseInt(claims.getId()));
        }
        return generateFailAuthResult();
    }

    @Override
    public TbUser selectUserByToken(String token) {
        Claims claims = getClaimsByToken(token);
        if (claims.getExpiration().getTime() - new Date().getTime() > 0) {
            return tbUserMapper.selectByPrimaryKey(Integer.valueOf(claims.getId()));
        }
        return null;
    }

    @Override
    public boolean sessionHasValidAuth(HttpSession session) {
        Object authResult = session.getAttribute(AUTH_RESULT_SESSION_NAME);
        return authResult != null && ((AuthResult) authResult).isSuccess() && (new Date().getTime() - ((AuthResult) authResult).getDeadLine().getTime() < 0);
    }

    @Override
    public TbUser selectUserBySession(HttpSession session) {
        if (sessionHasValidAuth(session)) {
            AuthResult authResult = (AuthResult) session.getAttribute(AUTH_RESULT_SESSION_NAME);
            return selectUserByToken(authResult.getToken());
        }
        return null;
    }

    @Override
    public TbUser createUser(TbUser tbUser) {
        if (!userNameIsRepeat(tbUser.getUsername())) {
            tbUserMapper.insert(tbUser);
            TbGroup root = new TbGroup();
            root.setUserId(tbUser.getId());
            root.setName("根目录");
            root.setRoot(true);
            tbGroupMapper.insert(root);
            return tbUser;
        }
        return null;
    }

    @Override
    public boolean userNameIsRepeat(String username) {
        TbUserCriteria criteria = new TbUserCriteria();
        criteria.or().andUsernameEqualTo(username);
        return tbUserMapper.selectByExample(criteria).size() > 0;
    }

    /**
     * 生成成功的鉴权结果
     *
     * @param userId 用户ID
     * @return 鉴权结果
     */
    private AuthResult generateSuccessAuthResult(int userId) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(TOKEN_KEY);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        Date now = new Date();
        Date expiration = new Date(now.getTime() + AuthConst.AUTH_DEAD_TIME);
        JwtBuilder builder = Jwts.builder().setId(String.valueOf(userId)).setExpiration(expiration).setIssuedAt(now).signWith(signatureAlgorithm, signingKey);
        AuthResult authResult = new AuthResult();
        authResult.setSuccess(true);
        authResult.setDeadLine(expiration);
        authResult.setToken(builder.compact());
        return authResult;
    }

    /**
     * 生成失败的鉴权结果
     *
     * @return 鉴权结果
     */
    private AuthResult generateFailAuthResult() {
        AuthResult authResult = new AuthResult();
        authResult.setToken("");
        authResult.setDeadLine(new Date());
        authResult.setSuccess(false);
        return authResult;
    }

    /**
     * 获取密钥链
     * @param token token
     * @return 密钥链
     */
    private Claims getClaimsByToken(String token) {
        return Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary(TOKEN_KEY))
                .parseClaimsJws(token).getBody();
    }
}
